#include "getput.h"

// 新增：清理套接字缓冲区
static void clear_socket_buffer(int sockfd) {
    char buf[1024];
    int ret;
    while ((ret = recv(sockfd, buf, sizeof(buf), MSG_DONTWAIT)) > 0);
}

// 新增：查询指定路径和用户的目录ID（用于获取parent_id）
static int get_dir_id(MYSQL *mysql, const char *dir_path, const char *username) {
    char query[1024];
    snprintf(query, sizeof(query),
             "SELECT id FROM virtual_directory WHERE user_name='%s' AND path='%s' AND type='d' AND is_deleted=0",
             username, dir_path);
    if (mysql_query(mysql, query) != 0) {
        fprintf(stderr, "查询目录ID失败: %s\n", mysql_error(mysql));
        return -1;
    }
    MYSQL_RES *res = mysql_store_result(mysql);
    if (res == NULL || mysql_num_rows(res) == 0) {
        mysql_free_result(res);
        return -1; // 父目录不存在
    }
    MYSQL_ROW row = mysql_fetch_row(res);
    int dir_id = atoi(row[0]);
    mysql_free_result(res);
    return dir_id;
}

static int split_path_components(const char *path, char **components, int max_components) {
    if (path == NULL || components == NULL || max_components <= 0) {
        return 0;
    }
    char *path_copy = strdup(path);
    if (path_copy == NULL) {
        return 0;
    }
    int count = 0;
    char *token = strtok(path_copy, "/");
    while (token != NULL && count < max_components) {
        components[count] = strdup(token);
        if (components[count] == NULL) {
            for (int i = 0; i < count; i++) free(components[i]);
            free(path_copy);
            return 0;
        }
        count++;
        token = strtok(NULL, "/");
    }
    free(path_copy);
    return count;
}

static void normalize_path(const char *input, char *output, size_t output_size) {
    if (input == NULL || output == NULL || output_size == 0) return;
    int is_absolute = (input[0] == '/') ? 1 : 0;
    is_absolute = 1; // 强制绝对路径

    char *components[MAX_PATH_COMPONENTS] = {0};
    int component_count = split_path_components(input, components, MAX_PATH_COMPONENTS);
    char *component_stack[MAX_PATH_COMPONENTS];
    int stack_size = 0;

    for (int i = 0; i < component_count; i++) {
        char *comp = components[i];
        if (strcmp(comp, ".") == 0) {
            free(comp);
            continue;
        } else if (strcmp(comp, "..") == 0) {
            if (stack_size > 0) {
                free(component_stack[stack_size - 1]);
                stack_size--;
            }
            free(comp);
        } else {
            if (stack_size < MAX_PATH_COMPONENTS) {
                component_stack[stack_size++] = comp;
            } else {
                free(comp);
            }
        }
    }

    output[0] = '\0';
    if (is_absolute) {
        strncat(output, "/", output_size - 1);
    }

    for (int i = 0; i < stack_size; i++) {
        size_t current_len = strlen(output);
        size_t comp_len = strlen(component_stack[i]);
        if (current_len + comp_len + 1 >= output_size) break;
        if (current_len > 1) {
            strncat(output, "/", output_size - current_len - 1);
            current_len++;
        }
        strncat(output, component_stack[i], output_size - current_len - 1);
    }

    if (stack_size == 0 && is_absolute && strlen(output) == 0) {
        strncat(output, "/", output_size - 1);
    }

    for (int i = 0; i < stack_size; i++) free(component_stack[i]);
}

static char* get_workdir_path(MYSQL *mysql, int top_id, char *workdir, size_t workdir_size,
                              char *username_out, size_t username_size) {
    char query[3000];
    snprintf(query, sizeof(query),
             "SELECT path, user_name FROM virtual_directory WHERE id=%d", top_id);
    if (mysql_query(mysql, query) != 0) {
        fprintf(stderr, "查询工作目录信息失败: %s\n", mysql_error(mysql));
        return NULL;
    }
    MYSQL_RES *res = mysql_store_result(mysql);
    if (res == NULL || mysql_num_rows(res) == 0) {
        fprintf(stderr, "未找到ID=%d对应的工作目录\n", top_id);
        if (res) mysql_free_result(res);
        return NULL;
    }
    MYSQL_ROW row = mysql_fetch_row(res);
    strncpy(workdir, row[0], workdir_size - 1);
    workdir[workdir_size - 1] = '\0';
    if (username_out != NULL && username_size > 0) {
        if (row[1] != NULL) {
            strncpy(username_out, row[1], username_size - 1);
        } else {
            username_out[0] = '\0';
        }
        username_out[username_size - 1] = '\0';
    }
    mysql_free_result(res);
    return workdir;
}

int process_path(char *filename, char *user_pwd, size_t pwd_size, MYSQL *mysql, my_stack *st, user_t *user) {
    if (st == NULL || st->stack_top == NULL || user == NULL) {
        fprintf(stderr, "栈为空、栈顶无效或用户信息为空\n");
        return -1;
    }
    char workdir[512] = "/";
    char temp[1024];
    char normalized[512];
    char current_username[128] = {0};

    int top_id = st->stack_top->id;

    if (!get_workdir_path(mysql, top_id, workdir, sizeof(workdir),
                          current_username, sizeof(current_username))) {
        fprintf(stderr, "ID=%d查询失败，使用默认根目录和当前用户\n", top_id);
        strncpy(workdir, "/", sizeof(workdir) - 1);
        strncpy(current_username, user->name, sizeof(current_username) - 1);
    }

    strncpy(user->name, current_username, sizeof(user->name) - 1);
    user->name[sizeof(user->name) - 1] = '\0';
    printf("当前工作目录用户名: %s\n", user->name);

    char current_dir[512];
    strncpy(current_dir, workdir, sizeof(current_dir) - 1);
    current_dir[sizeof(current_dir) - 1] = '\0';
    printf("当前工作目录路径（ID=%d查询结果）: %s\n", top_id, current_dir);

    if (filename[0] != '/') {
        if (strcmp(current_dir, "/") == 0) {
            snprintf(temp, sizeof(temp), "%s%s", current_dir, filename);
        } else {
            snprintf(temp, sizeof(temp), "%s/%s", current_dir, filename);
        }
    } else {
        strncpy(temp, filename, sizeof(temp) - 1);
    }
    temp[sizeof(temp) - 1] = '\0';

    normalize_path(temp, normalized, sizeof(normalized));

    strncpy(user_pwd, normalized, pwd_size - 1);
    user_pwd[pwd_size - 1] = '\0';
    printf("规范化后目标路径: %s\n", user_pwd);

    return 0;
}

int lookup_file_info(MYSQL *mysql, user_t *user, const char *path,
                     char *sha256_out, size_t sha256_size) {
    char query[3000];
    snprintf(query, sizeof(query),
             "SELECT sha_sum,parent_id,is_deleted,type FROM virtual_directory "
             "WHERE user_name='%s' AND path='%s'", user->name, path);
    if (mysql_query(mysql, query) != 0) {
        fprintf(stderr, "mysql_query error: %s\n", mysql_error(mysql));
        return -1;
    }
    MYSQL_RES *res = mysql_store_result(mysql);
    if (res == NULL) {
        fprintf(stderr, "mysql_store_result error: %s\n", mysql_error(mysql));
        return -1;
    }
    MYSQL_ROW row = mysql_fetch_row(res);
    if (row == NULL) {
        fprintf(stderr, "文件不存在（路径：%s）\n", path);
        mysql_free_result(res);
        return -1;
    }
    if (row[0]) {
        strncpy(sha256_out, row[0], sha256_size - 1);
        sha256_out[sha256_size - 1] = '\0';
    } else {
        *sha256_out = '\0';
    }
    user->parent_id = row[1] ? atoi(row[1]) : 0;
    user->is_deleted = row[2] ? atoi(row[2]) : 0;
    user->type = row[3] ? row[3][0] : 0;
    mysql_free_result(res);
    if (user->is_deleted) {
        fprintf(stderr, "文件已删除（路径：%s）\n", path);
        return -1;
    }
    if (user->type == 'd') {
        fprintf(stderr, "路径指向目录，不是文件（路径：%s）\n", path);
        return -1;
    }
    return 0;
}

static ssize_t safe_recv(int fd, void *buf, size_t len) {
    size_t total_recv = 0;
    char *p = (char *)buf;
    while (total_recv < len) {
        ssize_t ret = recv(fd, p + total_recv, len - total_recv, 0);
        if (ret <= 0) return ret;
        total_recv += ret;
    }
    return total_recv;
}

static ssize_t safe_send(int fd, void *buf, size_t len) {
    size_t total = 0;
    char *p = (char *)buf;
    while (total < len) {
        ssize_t ret = send(fd, p + total, len - total, MSG_NOSIGNAL);
        if (ret <= 0) return ret;
        total += ret;
    }
    return total;
}
// 辅助函数：计算文件前N字节的SHA256哈希（用于断点续传验证）
static void calculate_partial_sha256(int fd, char *sha_out, off_t len) {
    if (sha_out == NULL || len <= 0) return;
    memset(sha_out, 0, SHA256_DIGEST_LENGTH * 2 + 1);

    SHA256_CTX ctx;
    SHA256_Init(&ctx);
    char buf[8192];
    off_t total_read = 0;

    // 定位到文件开头，计算前len字节的哈希
    if (lseek(fd, 0, SEEK_SET) == -1) {
        fprintf(stderr, "计算部分哈希时定位文件失败: %s\n", strerror(errno));
        return;
    }

    while (total_read < len) {
        ssize_t r = read(fd, buf, sizeof(buf));
        if (r < 0) {
            fprintf(stderr, "计算部分哈希时读取文件失败: %s\n", strerror(errno));
            return;
        }
        if (r == 0) break; // 文件实际长度小于len

        // 若剩余未读部分小于缓冲区大小，只取需要的长度
        off_t read_len = (total_read + r) > len ? (len - total_read) : r;
        SHA256_Update(&ctx, buf, read_len);
        total_read += read_len;
    }

    unsigned char digest[SHA256_DIGEST_LENGTH];
    SHA256_Final(digest, &ctx);

    // 转换为十六进制字符串
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        snprintf(sha_out + 2 * i, 3, "%02x", digest[i]);
    }
}

int getsfile(int cur_user_id, int clientfd, MYSQL *mysql, user_t *user, char *filename, my_stack *st) {
    trans_train_t train;
    train_t cmdtrain;
    char real_path[1024];
    off_t offset = 0, filesize = 0, remaining = 0;
    int fd, ret;

    // 清理套接字缓冲区，避免残留数据干扰
    clear_socket_buffer(clientfd);
    printf("处理下载请求，文件: %s\n", filename);

    // 处理路径（转换为实际虚拟路径）
    if (process_path(filename, user->pwd, sizeof(user->pwd), mysql, st, user) != 0) {
        strcpy(train.data, "路径无效"); 
        train.length = strlen(train.data);
        safe_send(clientfd, &train.length, sizeof(train.length));
        safe_send(clientfd, train.data, train.length);
        printf("路径无效，拒绝下载\n");
        return -1;
    }

    // 通知客户端路径有效
    strcpy(train.data, "路径有效"); 
    train.length = strlen(train.data);
    safe_send(clientfd, &train.length, sizeof(train.length));
    safe_send(clientfd, train.data, train.length);
    printf("路径有效，继续处理下载\n");

    // 校验文件是否存在并获取SHA256
    if (lookup_file_info(mysql, user, user->pwd, user->sha256, sizeof(user->sha256)) != 0) {
        strcpy(train.data, "文件不存在或不可用"); 
        train.length = strlen(train.data);
        safe_send(clientfd, &train.length, sizeof(train.length));
        safe_send(clientfd, train.data, train.length);
        printf("文件不存在，拒绝下载\n");
        return -1;
    }

    // 通知客户端文件存在
    strcpy(train.data, "文件存在"); 
    train.length = strlen(train.data);
    safe_send(clientfd, &train.length, sizeof(train.length));
    safe_send(clientfd, train.data, train.length);
    printf("文件存在，SHA256: %s\n", user->sha256);

    // 打开实际文件（只读，不截断，保留断点续传基础）
    snprintf(real_path, sizeof(real_path), "%s/%s", ROOTDIR, user->sha256);
    fd = open(real_path, O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "打开文件失败: %s, 路径: %s\n", strerror(errno), real_path);
        strcpy(train.data, "文件读取失败");
        train.length = strlen(train.data);
        safe_send(clientfd, &train.length, sizeof(train.length));
        safe_send(clientfd, train.data, train.length);
        return -1;
    }

    // 获取文件大小
    struct stat stbuf; 
    if (fstat(fd, &stbuf) == -1) {
        fprintf(stderr, "获取文件信息失败: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    filesize = stbuf.st_size;
    printf("文件实际大小: %lld 字节\n", (long long)filesize);

    // 接收客户端的断点位置（偏移量）
    if ((ret = safe_recv(clientfd, &cmdtrain.type, sizeof(cmdtrain.type))) <= 0 ||
        (ret = safe_recv(clientfd, &cmdtrain.length, sizeof(cmdtrain.length))) <= 0) {
        close(fd); 
        fprintf(stderr, "接收客户端断点信息失败\n");
        return -1;
    }

    // 校验偏移量有效性
    offset = (off_t)cmdtrain.length;
    if (offset < 0 || offset > filesize) {
        offset = 0; // 无效偏移量，从头开始
        printf("客户端提供的偏移量无效（%lld），重置为0\n", (long long)cmdtrain.length);
    } else {
        printf("客户端请求从 %lld 字节开始下载（断点续传）\n", (long long)offset);
    }

    // 通知客户端偏移量有效
    strcpy(train.data, "偏移量有效"); 
    train.length = strlen(train.data);
    safe_send(clientfd, &train.length, sizeof(train.length));
    safe_send(clientfd, train.data, train.length);

    // 发送完整文件的SHA256（供最终校验）
    size_t sha_len = strlen(user->sha256);
    safe_send(clientfd, &sha_len, sizeof(sha_len));
    safe_send(clientfd, user->sha256, sha_len);

    // 发送文件元数据（偏移量、总大小、剩余大小）
    remaining = filesize - offset;
    safe_send(clientfd, &offset,   sizeof(offset));
    safe_send(clientfd, &filesize, sizeof(filesize));
    safe_send(clientfd, &remaining, sizeof(remaining));
    printf("发送文件元数据: 偏移量=%lld, 总大小=%lld, 剩余=%lld\n", 
           (long long)offset, (long long)filesize, (long long)remaining);

    // 核心：若客户端有已下载部分（偏移量>0），发送该部分的哈希供验证
    if (offset > 0) {
        char part_sha[65] = {0};
        calculate_partial_sha256(fd, part_sha, offset); // 计算0~offset部分的哈希
        size_t part_sha_len = strlen(part_sha);
        safe_send(clientfd, &part_sha_len, sizeof(part_sha_len));
        safe_send(clientfd, part_sha, part_sha_len);
        printf("发送断点前部分哈希（0~%lld字节）: %s\n", (long long)offset, part_sha);
    }

    // 定位到偏移量位置，准备发送剩余数据
    if (lseek(fd, offset, SEEK_SET) == -1) {
        fprintf(stderr, "定位到偏移量失败: %s\n", strerror(errno));
        close(fd);
        return -1;
    }

    // 发送文件数据（使用8KB缓冲区提升速度）
    off_t sent = 0;
    char buf[8192];
    printf("开始发送文件数据（从偏移量%lld开始）...\n", (long long)offset);
    while (sent < remaining) {
        ssize_t r = read(fd, buf, sizeof(buf));
        if (r < 0) { // 读取错误
            fprintf(stderr, "读取文件失败: %s\n", strerror(errno));
            close(fd);
            return -1;
        }
        if (r == 0) { // 文件被截断，数据不足
            fprintf(stderr, "文件提前结束，预期发送%lld字节，实际可发送%lld字节\n",
                    (long long)remaining, (long long)sent);
            close(fd);
            return -1;
        }

        // 确保数据完整发送
        if (safe_send(clientfd, buf, r) != r) {
            fprintf(stderr, "发送数据失败\n");
            close(fd);
            return -1;
        }
        sent += r;
    }
    close(fd); // 数据发送完成，关闭文件

    // 校验发送结果并通知客户端
    if (sent == remaining) {
        printf("文件发送完成，共发送 %lld 字节\n", (long long)sent);
        // 发送传输完成确认
        train.length = strlen("传输完成");
        strcpy(train.data, "传输完成");
        safe_send(clientfd, &train.length, sizeof(train.length));
        safe_send(clientfd, train.data, train.length);
        // 记录日志
        log_info(mysql, cur_user_id, "gets", 1, filename);
        return 0;
    } else {
        fprintf(stderr, "文件发送不完整，预期 %lld 字节，实际发送 %lld 字节\n", 
                (long long)remaining, (long long)sent);
        // 发送传输失败通知
        train.length = strlen("传输失败");
        strcpy(train.data, "传输失败");
        safe_send(clientfd, &train.length, sizeof(train.length));
        safe_send(clientfd, train.data, train.length);
        return -1;
    }
}

static void get_residual_path(const char *sha256, char *residual_path, size_t size) {
    snprintf(residual_path, size, "%s/%s.part", ROOTDIR, sha256);
}

void calculate_file_sha256(const char *filename, char *sha256_out, size_t size) {
    if (size < SHA256_DIGEST_LENGTH * 2 + 1) {
        fprintf(stderr, "SHA256输出缓冲区太小\n");
        *sha256_out = '\0';
        return;
    }
    memset(sha256_out, 0, size);

    int fd = open(filename, O_RDONLY);
    ERROR_CHECK(fd, -1, "open failed");

    struct stat statbuf;
    fstat(fd, &statbuf);
    size_t filesize = statbuf.st_size;

    unsigned char *file_data = (unsigned char *)mmap(
                                                     NULL, filesize, PROT_READ, MAP_SHARED, fd, 0
                                                    );
    ERROR_CHECK((long)file_data, (long)MAP_FAILED, "mmap failed");

    SHA256_CTX ctx;
    SHA256_Init(&ctx);
    SHA256_Update(&ctx, file_data, filesize);
    unsigned char digest[SHA256_DIGEST_LENGTH];
    SHA256_Final(digest, &ctx);

    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        snprintf(sha256_out + 2*i, 3, "%02x", digest[i]);
    }
    sha256_out[SHA256_DIGEST_LENGTH * 2] = '\0';

    munmap(file_data, filesize);
    close(fd);
}

int insert_file_info(MYSQL *mysql, user_t *user, const char *path, const char *sha256 ) {
    char query[3000];
    char username[128] = {0};

    if (user == NULL || user->name[0] == '\0') {
        fprintf(stderr, "错误：用户信息中无有效用户名\n");
        return -1;
    }
    strncpy(username, user->name, sizeof(username)-1);
    username[sizeof(username)-1] = '\0';

    // 确保文件名是路径的最后一部分
    const char *filename = strrchr(path, '/');
    if (filename == NULL) {
        filename = path;
    } else {
        filename++; // 跳过斜杠
    }

    snprintf(query, sizeof(query),
             "INSERT INTO virtual_directory "
             "(id, file_name, user_name, parent_id, type, path, is_deleted, sha_sum) "
             "VALUES (%d, '%s', '%s', %d, 'f', '%s', 0, '%s')",
             user->id,
             filename,
             username,
             user->parent_id,
             path,
             sha256);

    printf("执行SQL: %s\n", query);
    int result = mysql_query(mysql, query);
    if (result != 0) {
        fprintf(stderr, "插入文件信息失败: %s\n", mysql_error(mysql));
    }
    return result;
}

void sanitize_filename(char *filename) {
    if (!filename) return;

    char *last_slash = strrchr(filename, '/');
    char *name_part;

    if (last_slash != NULL) {
        name_part = last_slash + 1;
    } else {
        name_part = filename;
    }

    for (char *p = name_part; *p; p++) {
        if (!isalnum((unsigned char)*p) && *p != '.' && *p != '_' && *p != '-') {
            *p = '_';
        }
    }
}

static int check_file_by_sha256(MYSQL *mysql, const char *sha256) {
    char query[3000];
    snprintf(query, sizeof(query),
             "SELECT 1 FROM virtual_directory WHERE sha_sum='%s' AND is_deleted=0 AND type='f'",
             sha256);
    if (mysql_query(mysql, query) != 0) {
        fprintf(stderr, "查询SHA256失败: %s\n", mysql_error(mysql));
        return 0;
    }
    MYSQL_RES *res = mysql_store_result(mysql);
    if (res == NULL) {
        fprintf(stderr, "存储结果失败: %s\n", mysql_error(mysql));
        return 0;
    }
    int exists = (mysql_num_rows(res) > 0) ? 1 : 0;
    mysql_free_result(res);
    return exists;
}

int putsfile(int cur_user_id, int clientfd, MYSQL* mysql, user_t *user, char* filename, my_stack *st) {
    printf("处理上传，文件: %s\n", filename);
    sanitize_filename(filename);
    strncpy(user->filename, filename, sizeof(user->filename)-1);

    trans_train_t train;
    train_t cmdtrain;
    char residual[1024], final_path[1024];
    off_t offset=0, filesize=0, remaining=0;
    int ret, fd;

    if (process_path(filename, user->pwd, sizeof(user->pwd), mysql, st, user) != 0) {
        fprintf(stderr, "路径处理失败\n");
        return -1;
    }

    // 提取父目录路径
    char parent_path[512];
    const char *last_slash = strrchr(user->pwd, '/');
    if (last_slash == user->pwd) { // 根目录下的文件，父目录是/
        strcpy(parent_path, "/");
    } else if (last_slash != NULL) {
        size_t parent_len = last_slash - user->pwd;
        strncpy(parent_path, user->pwd, parent_len);
        parent_path[parent_len] = '\0';
    } else {
        strcpy(parent_path, "/"); // 异常处理
    }

    // 查询父目录ID
    user->parent_id = get_dir_id(mysql, parent_path, user->name);
    if (user->parent_id == -1) {
        fprintf(stderr, "父目录不存在或查询失败，路径: %s\n", parent_path);
        return -1;
    }
    printf("父目录路径: %s, ID: %d\n", parent_path, user->parent_id);

    strcpy(train.data, "路径有效"); 
    train.length = strlen(train.data);
    safe_send(clientfd, &train.length, sizeof(train.length));
    safe_send(clientfd, train.data, train.length);
    printf("已发送路径有效确认\n");

    // 接收客户端发送的SHA256和文件大小
    safe_recv(clientfd, &train.length, sizeof(train.length));
    safe_recv(clientfd, train.data, train.length);
    memcpy(user->sha256, train.data, train.length);
    user->sha256[train.length] = '\0'; // 确保字符串结束
    printf("收到客户端SHA256: %s\n", user->sha256);

    safe_recv(clientfd, &train.length, sizeof(train.length));
    safe_recv(clientfd, &filesize, train.length);
    printf("收到客户端文件大小: %lld 字节\n", (long long)filesize);

    // 极速秒传核心：检查服务端是否已存在相同SHA256的文件
    if (check_file_by_sha256(mysql, user->sha256)) {
        printf("极速秒传：文件已存在（SHA256: %s），无需传输\n", user->sha256);

        // 通知客户端无需传输，直接完成
        strcpy(train.data, "准备接收数据"); 
        train.length = strlen(train.data);
        safe_send(clientfd, &train.length, sizeof(train.length));
        safe_send(clientfd, train.data, train.length);

        cmdtrain.type = SEND_REUPLOAD;
        cmdtrain.length = filesize; // 告知客户端已完成所有数据
        safe_send(clientfd, &cmdtrain.type, sizeof(cmdtrain.type));
        safe_send(clientfd, &cmdtrain.length, sizeof(cmdtrain.length));

        // 直接插入数据库记录（关联现有文件）
        snprintf(final_path, sizeof(final_path), "%s/%s", ROOTDIR, user->sha256);
        if (insert_file_info(mysql, user, user->pwd, user->sha256) != 0) {
            fprintf(stderr, "插入文件信息失败\n");
            return -1;
        }

        printf("极速秒传完成，文件路径: %s\n", user->pwd);
        return 0;
    }

    // 非妙传场景：正常处理断点续传
    get_residual_path(user->sha256, residual, sizeof(residual));
    struct stat stbuf;
    if (stat(residual, &stbuf) == 0 && stbuf.st_size > 0 && stbuf.st_size < filesize) {
        offset = stbuf.st_size;
        printf("发现部分上传文件，大小: %lld 字节，将从该位置续传\n", (long long)offset);
    } else {
        offset = 0;
        unlink(residual); // 删除无效残留文件
        printf("未发现有效残留文件，将从头开始接收\n");
    }
    remaining = filesize - offset;

    // 通知客户端续传偏移量
    strcpy(train.data, "准备接收数据"); 
    train.length = strlen(train.data);
    safe_send(clientfd, &train.length, sizeof(train.length));
    safe_send(clientfd, train.data, train.length);

    cmdtrain.type = SEND_REUPLOAD;
    cmdtrain.length = offset;
    safe_send(clientfd, &cmdtrain.type, sizeof(cmdtrain.type));
    safe_send(clientfd, &cmdtrain.length, sizeof(cmdtrain.length));
    printf("已通知客户端从 %lld 字节开始上传，剩余 %lld 字节\n", (long long)offset, (long long)remaining);

    // 接收文件数据
    fd = open(residual, O_CREAT | O_RDWR, 0666);
    if (fd < 0) {
        fprintf(stderr, "创建临时文件失败: %s\n", strerror(errno));
        return -1;
    }
    if (lseek(fd, offset, SEEK_SET) == -1) {
        fprintf(stderr, "设置文件偏移量失败: %s\n", strerror(errno));
        close(fd);
        return -1;
    }

    off_t recvd = 0;
    char buf[4096];
    printf("开始接收文件数据...\n");
    while (recvd < remaining) {
        ssize_t r = recv(clientfd, buf, sizeof(buf), 0);
        if (r <= 0) { 
            fprintf(stderr, "接收文件数据失败: %s\n", strerror(errno));
            close(fd); 
            return -1; 
        }
        if (write(fd, buf, r) != r) {
            fprintf(stderr, "写入文件失败: %s\n", strerror(errno));
            close(fd);
            return -1;
        }
        recvd += r;
    }
    close(fd);

    if (recvd != remaining) {
        fprintf(stderr, "接收不完整（预期 %lld, 实际 %lld）\n", (long long)remaining, (long long)recvd);
        return -1;
    }

    printf("文件接收完成，共接收 %lld 字节\n", (long long)recvd);

    // 重命名临时文件为最终文件
    snprintf(final_path, sizeof(final_path), "%s/%s", ROOTDIR, user->sha256);
    if (rename(residual, final_path) != 0) {
        fprintf(stderr, "重命名文件失败: %s\n", strerror(errno));
        return -1;
    }

    // 插入数据库记录
    if (insert_file_info(mysql, user, user->pwd, user->sha256) != 0) {
        fprintf(stderr, "插入文件信息失败\n");
        return -1;
    }

    printf("上传成功，文件已保存至: %s\n", final_path);
    return 0;
}
